package quotactl

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/shirou/gopsutil/v3/disk"
	"gitlab.local/golibrary/linux"
	"gitlab.local/golibrary/user"
	"gitlab.local/golibrary/utils"
	"regexp"
	"strconv"
	"strings"
)

func (q *Quota) Set(name string, quotaList []UserQuota) error {
	validUser, err := q.checkValidUser(name)
	if err != nil {
		return err
	}
	role := "-u"
	if validUser == "group" {
		role = "-g"
		name = name[1:]
	}
	df, err := disk.Partitions(false)
	if err != nil {
		return errors.Wrap(err, fmt.Sprintf("setquota %s failed", name))
	}
	for _, item := range df {
		//只针对ext4文件系统设置容量限制
		if item.Fstype != "ext4" {
			continue
		}
		quota, ok := q.findQuota(quotaList, item.Device)
		if !ok {
			continue
		}
		//把前端传过来的GB转换成KB
		quota = quota * 1024 * 1024
		//纯数字超过20位的时候会设置失败，x86命令没报错，只是在标准错误输出中体现，修改为当超时20位的时候使用uid
		if role == "-u" {
			if len(name) >= 20 {
				ok, err = regexp.MatchString("^\\d{20,}$", name)
				if err == nil && ok {
					u, err := user.Lookup(name)
					if err != nil {
						return fmt.Errorf("find user failed %v", err)
					}
					name = u.Uid
				}
			}
		}
		cmd := fmt.Sprintf("setquota %s %s 0 %d 0 0 %s", role, name, quota, item.Device)
		if _, err = utils.ShellExec(cmd); err != nil {
			return errors.Wrap(err, fmt.Sprintf("setquota %s failed", name))
		}
	}
	return nil
}

func (q *Quota) Get(name string) (result []UserQuota) {
	result = make([]UserQuota, 0)
	var cmder string
	if !strings.HasPrefix(name, "@") {
		//纯数字超过20位的时候会设置失败，x86命令没报错，只是在标准错误输出中体现，修改为当超时20位的时候使用uid
		if len(name) >= 20 {
			ok, err := regexp.MatchString("^\\d{20,}$", name)
			if err == nil && ok {
				u, err := user.Lookup(name)
				if err != nil {
					return result
				}
				name = u.Uid
			}
		}
		cmder = fmt.Sprintf("quota -uvwl %s | grep /dev/ |awk '{print $1,$4}'", name)
	} else {
		cmder = fmt.Sprintf("quota -gvwl %s | grep /dev/ |awk '{print $1,$4}'", name[1:])
	}
	spaceRule, _ := regexp.Compile(`[\s\t ]+`)
	contents, _ := utils.ShellExec(cmder)
	for _, line := range strings.Split(contents, "\n") {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		fp := spaceRule.Split(line, -1)
		value, _ := strconv.ParseUint(fp[1], 10, 64)
		di := linux.GetDeviceInfo(fp[0])
		if di != nil {
			result = append(result, UserQuota{
				Device: di.Device,
				Limit:  value / 1024 / 1024,
				Total:  di.Total / 1024 / 1024,
			})
		}
	}
	return
}

func (q *Quota) findQuota(list []UserQuota, device string) (uint64, bool) {
	for _, quota := range list {
		if quota.Device == device {
			return quota.Limit, true
		}
	}
	return 0, false
}

func (q *Quota) checkValidUser(name string) (result string, errResult error) {
	result = "user"
	if strings.HasPrefix(name, "@") {
		result = "group"
		_, errResult = user.LookupGroup(name[1:])
	} else {
		_, errResult = user.Lookup(name)
	}
	return
}
